package com.tanxiaoyao.wecon.net;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

import com.tanxiaoyao.wecon.R;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.util.Log;

/**
 * 蓝牙连接服务，包含三条线程 1.监听接收线程 2.发送线程 3.与其他设备连接线程
 * 
 * @author tanxiaoyao(junlidev@gmail.com)
 * 
 */
public class BluetoothConnectService {
	// DEBUG标志
	private static final String TAG = "tanxiaoyao-service";
	private static final Boolean ISDEBUG = true;

	// 创建服务时的名字记录常量
	private static final String CONNECT_NAME = "connect_wecon";

	// 常用的串口UUID
	private static final UUID SP_UUID = UUID
			.fromString("00001101-0000-1000-8000-00805F9B34FB");
	
	//全局连接器
	private GlobalConnectHelper globalConnectHelper;

	// 服务对象
	private final BluetoothAdapter bluetoothAdapter;
	private AcceptThread accepThread; // 接收线程
	private ConnectThread connectThread; // 设备连接线程
	private MonitorThread monitorThread; // 连接后管理收/发的监听线程
	private int state; // 服务的状态标志量（变量）

	// 服务的各种状态标志常量(可能被外部访问，设置为公开)
	public static final int STATE_NONE = 0; // 闲置状态
	public static final int STATE_LISTEN = 1; // 监听状态
	public static final int STATE_CONNECTING = 2; // 正在连接状态
	public static final int STATE_CONNECTED = 3; // 已连接状态
	
	/**
	 * 构造方法，为外界提供一个连接服务，但是不绑定handler
	 */
	public BluetoothConnectService() {
		// 获取适配器，初始化状态，制定Handler
		bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
		state = STATE_NONE;
		globalConnectHelper = GlobalConnectHelper.getGlobalConnector();
	}
	

	// //////////////////////////////////////////////////
	// synchronized关键字：将可能访问成员变量的方法设置为synchronized
	// 就可以确保当前只有一个方法能够取得锁并执行，防止该对象同时执行两个或两个以
	// 上的方法同时修改成员变量而造成冲突，本例中存在多个线程，他们可能会同时访问
	// 变量，所以访问方法均设置为该属性
	// //////////////////////////////////////////////////

	/**
	 * 设置服务状态
	 * 
	 * @param state
	 *            服务标志量
	 */
	private synchronized void setState(int state) {
		if (ISDEBUG)
			Log.d(TAG, "+++++setState() " + this.state + " -> " + state
					+ "+++++");
		this.state = state;
		//通知UI进行刷新（标签是：状态发生改变）
		globalConnectHelper.sendMsgToActivity(GlobalConnectHelper.MESSAGE_STATE_CHANGE, state, -1, null);
	}

	/**
	 * 返回服务状态
	 * 
	 * @return 状态标识
	 */
	public synchronized int getState() {
		return this.state;
	}

	/**
	 * 服务总开启函数
	 */
	public synchronized void start() {
		if (ISDEBUG)
			Log.d(TAG, "+++++connect service started+++++");

		// 首先终止其他任何连接线程
		if (this.connectThread != null) {
			connectThread.cancel();
			// 销毁此链接线程
			connectThread = null;
		}
		// 销毁正在连接的收发线程
		if (monitorThread != null) {
			monitorThread.cancel();
			monitorThread = null;
		}

		// 更新状态为监听状态
		setState(STATE_LISTEN);

		// 开启接受线程监听连入信息
		if (this.accepThread == null) {
			this.accepThread = new AcceptThread();
			this.accepThread.start();
		}
	}

	/**
	 * 连接指定的蓝牙设备
	 * 
	 * @param bluetoothDevice
	 *            北连接的蓝牙设备
	 */
	public synchronized void connect(BluetoothDevice bluetoothDevice) {
		if (ISDEBUG)
			Log.d(TAG, "+++++connect to:" + bluetoothDevice);

		// 先取消所有此时尝试连接的线程
		if (this.state == STATE_CONNECTING) {
			if (this.connectThread != null) {
				this.connectThread.cancel();
				this.connectThread = null;
			}
		}
		// 与前面一样，取消已经连接的线程
		if (this.monitorThread != null) {
			this.monitorThread.cancel();
			monitorThread = null;
		}
		// 开始正式连接设备
		this.connectThread = new ConnectThread(bluetoothDevice);
		this.connectThread.start();
		// 更新状态
		setState(STATE_CONNECTING);
	}

	/**
	 * 已经连接之后的监听方法
	 * 
	 * @param bluetoothSocket
	 *            连接所使用的socket对象
	 * @param bluetoothDevice
	 *            连接的目标设备
	 */
	public synchronized void connected(BluetoothSocket bluetoothSocket,
			BluetoothDevice bluetoothDevice) {
		if (ISDEBUG)
			Log.d(TAG, "+++++connected+++++");

		// 结束连接过程中所使用的线程
		if (this.connectThread != null) {
			this.connectThread.cancel();
			this.connectThread = null;
		}
		// 结束已有的连接收发线程
		if (this.monitorThread != null) {
			this.monitorThread.cancel();
			this.monitorThread = null;
		}
		// 结束接受线程
		if (this.accepThread != null) {
			this.accepThread.cancel();
			this.accepThread = null;
		}
		// 开启连接收发监视线程用于管理收发信息
		this.monitorThread = new MonitorThread(bluetoothSocket);
		this.monitorThread.start();
		//发送连接上的设备名称到主UI
		globalConnectHelper.sendMsgToActivity(GlobalConnectHelper.MESSAGE_DEVICE_NAME, -1, -1, bluetoothDevice.getName());
		// 更新状态标识
		setState(STATE_CONNECTED);
	}

	/**
	 * 终止所有链接
	 */
	public synchronized void stop() {
		if (ISDEBUG)
			Log.d(TAG, "+++++stop all+++++");

		if (this.connectThread != null) {
			this.connectThread.cancel();
			this.connectThread = null;
		}

		if (this.monitorThread != null) {
			this.monitorThread.cancel();
			this.monitorThread = null;
		}

		if (this.accepThread != null) {
			this.accepThread.cancel();
			this.accepThread = null;
		}
		// 更新标志
		setState(STATE_NONE);
	}

	/**
	 * 写输出流方法
	 * 
	 * @param out
	 *            输出字节
	 */
	public void write(byte[] out) {
		// 创建临时变量
		MonitorThread tempThread;
		// 同步取得该线程实例,同时只能有一个线程写数据
		synchronized (this) {
			if (this.state != STATE_CONNECTED)
				return;
			tempThread = this.monitorThread;
		}
		// 写数据
		tempThread.write(out);
	}

	/**
	 * 连接失败处理
	 */
	private void connectionFailed() {
		//向主UI报告连接失败
		globalConnectHelper.sendMsgToActivity(GlobalConnectHelper.MESSAGE_TOAST, R.string.connection_failed, -1, null);
		// 重启服务至监听状态
		BluetoothConnectService.this.start();
	}

	/**
	 * 连接丢失处理方法
	 */
	private void connectionLost() {
		//发送消息给UI
		globalConnectHelper.sendMsgToActivity(GlobalConnectHelper.MESSAGE_TOAST, R.string.connection_lost, -1, null);
		// 重启连接服务
		BluetoothConnectService.this.start();
	}

	// /////////////////////////////////////////////////
	// 下面是关键的三个线程具体实现
	// /////////////////////////////////////////////////

	/**
	 * 连接接受线程，接受外部设备的连接请求
	 * 
	 * @author tanxiaoyao(junlidev@gmail.com)
	 * 
	 */
	private class AcceptThread extends Thread {
		// 本地服务socket
		private final BluetoothServerSocket bluetoothServerSocket;

		/**
		 * 构造方法
		 */
		public AcceptThread() {
			BluetoothServerSocket tempBluetoothServerSocket = null;
			// 新建一个监听线程
			try {
				tempBluetoothServerSocket = bluetoothAdapter
						.listenUsingRfcommWithServiceRecord(CONNECT_NAME,
								SP_UUID);
			} catch (IOException e) {
				if (ISDEBUG)
					Log.e(TAG, "+++++create listen socket failed+++++");
			}

			this.bluetoothServerSocket = tempBluetoothServerSocket;
		}

		/**
		 * 运行方法
		 */
		public void run() {
			if (ISDEBUG)
				Log.d(TAG, "+++++acceptThread begin:" + this + "+++++");
			// 为当前线程命名
			setName("AcceptThread");
			// 客户端socket
			BluetoothSocket bluetoothSocket;
			// 如果本机没有连接则监听本机被连接的端口(死循环直到连接成功)
			while (BluetoothConnectService.this.state != STATE_CONNECTED) {
				try {
					// 返回一个链接成功的bluetoothSocket
					bluetoothSocket = bluetoothServerSocket.accept();
					//上一句阻塞过后说明已经正在连接设备了，这样就不用去掉下面的break
					setState(STATE_CONNECTING);
				} catch (Exception e) {
					Log.e(TAG, "+++++接受监听异常+++++");
					// 跳出死循环
					break;
				}
				// 跳出循环后判断是否成功
				if (bluetoothSocket != null) {
					synchronized (BluetoothConnectService.this) {
						switch (BluetoothConnectService.this.state) {
						case STATE_LISTEN:
							// 这里直接break掉吧，有必要往下？
							break;
						case STATE_CONNECTING:
							// 正常情况，开始收发监听线程
							connected(bluetoothSocket,
									bluetoothSocket.getRemoteDevice());
							break;
						case STATE_NONE:
							break;
						case STATE_CONNECTED:
							// 没准备好或是已经连接上？终止新的前面得到的socket
							try {
								bluetoothSocket.close();
							} catch (IOException e) {
								if (ISDEBUG)
									Log.e(TAG, "+++++关闭不必要的线程失败+++++");
							}
							break;
						default:
							break;
						}
					}
				}
			}
			if (ISDEBUG)
				Log.d(TAG, "+++++acceptThread end+++++");
		}

		/**
		 * 结束线程方法
		 */
		public void cancel() {
			if (ISDEBUG)
				Log.d(TAG, "+++++cancel therad:" + this + "+++++");
			try {
				this.bluetoothServerSocket.close();
			} catch (IOException e) {
				if (ISDEBUG)
					Log.e(TAG, "+++++close bluetoothServerSocket:"
							+ this.bluetoothServerSocket + "failed!+++++");
			}
		}

	}


	/**
	 * 一个尝试连接外部设备的线程，不管成功或失败会执行到底
	 * @author tanxiaoyao(junlidev@gmail.com)
	 *
	 */
	private class ConnectThread extends Thread{
		//局部变量（这里跟前面的有点混乱了，我改变一下命名方式）
		private final BluetoothSocket connectBluetoothSocket;
		private final BluetoothDevice connectBluetoothDevice;
		
		/**
		 * 构造方法，连接一个外部设备
		 * @param device	要连接的外部设备
		 */
		public ConnectThread(BluetoothDevice device){
			this.connectBluetoothDevice = device;
			BluetoothSocket tempSocket = null;
			
			//从要连接的设备处获得一个socket
			try {
				tempSocket = device.createRfcommSocketToServiceRecord(SP_UUID);
			} catch (IOException e) {
				if(ISDEBUG) Log.e(TAG, "+++++connect thread get socket failed!+++++");
			}
			//缓存进本地对象
			this.connectBluetoothSocket = tempSocket;
		}
		
		/**
		 * 线程运行方法
		 */
		public void run() {
			if(ISDEBUG) Log.d(TAG, "+++++connect thread begin!+++++");
			//线程命名
			setName("connectThread");
			//尝试连接时关闭蓝牙的可发现，它会减慢连接速度
			BluetoothConnectService.this.bluetoothAdapter.cancelDiscovery();
			//尝试连接(这是一个阻塞过程直到连接返回成功)
			try {
				this.connectBluetoothSocket.connect();
			} catch (IOException e) {
				try {
					this.connectBluetoothSocket.close();
				} catch (IOException e2) {
					if(ISDEBUG) Log.e(TAG, "+++++connect thread failed to close socket!+++++");
				}
				//连接失败处理
				connectionFailed();
				return;
			}
			
			//连接成功完成，复位连接线程
			synchronized (BluetoothConnectService.this) {
				BluetoothConnectService.this.connectThread = null;
			}
			//随后开启成功后的收发线程
			connected(connectBluetoothSocket, connectBluetoothDevice);
		}
		
		/**
		 * 结束连接线程
		 */
		public void cancel(){
			try {
				//关闭socket,释放资源
				connectBluetoothSocket.close();
			} catch (IOException e) {
				if(ISDEBUG) Log.e(TAG, "+++++close connect thread failed!+++++");
			}
		}
	}
	
	/**
	 * 信息收发监控线程
	 * @author tanxiaoyao(junlidev@gmail.com)
	 *
	 */
	private class MonitorThread extends Thread{
		//局部变量
		private final BluetoothSocket monitorBluetoothSocket;
		private final InputStream monitorInputStream;
		private final OutputStream monitorOutputStream;
		
		/**
		 * 构造方法，为外界提供一个监控方法
		 * @param socket
		 */
		public MonitorThread(BluetoothSocket socket){
			if(ISDEBUG) Log.d(TAG, "+++++monitor thread created!+++++");
			//变量赋值
			monitorBluetoothSocket = socket;
			InputStream tempInputStream = null;
			OutputStream tempOutputStream = null;
			
			//从socket得到输入输出流
			try {
				tempInputStream = socket.getInputStream();
				tempOutputStream = socket.getOutputStream();
			} catch (IOException e) {
				if(ISDEBUG) Log.e(TAG, "++++++get in/output stream failed!+++++");
			}
			
			//读取后缓存进对象
			monitorInputStream = tempInputStream;
			monitorOutputStream = tempOutputStream;
		}
		
		/**
		 * 线程运行
		 */
		public void run(){
			if(ISDEBUG) Log.d(TAG, "+++++monitor thread run!+++++");
			//字节缓冲数组
			byte[] buffer = new byte[40];
			int bytes;
			//保持监听输入流(这在线程中是一个死循环)
			while (true) {
				try {
					//从输入流中读取信息（这个能从固定长度的数组中读出包含信息的长度？）
					//从源码中我们可以看到，通过校验数组中的字符可以得出消息长度
					//这也就闲置了一句话不能超过1024byte，否者应该扩大数组长度
					//或者分包校验并设置停止校验位
					bytes = monitorInputStream.read(buffer);
					//将读取到的信息发送到主线程处理
					if(ISDEBUG) Log.d(TAG, "recive : " + new String(buffer,0,bytes));
//					handler.obtainMessage(ActivityChat.MESSAGE_READ,bytes,-1,buffer).sendToTarget();
					//直接将信息发送到连接管理助手
					globalConnectHelper.reciveMess(new String(buffer,0,bytes));
				} catch (IOException e) {
					if(ISDEBUG) Log.e(TAG, "+++++connection lost,read bytes failed!+++++");
					//连接丢失处理
					connectionLost();
					break;
				}
			}
		}
		
		/**
		 * 输出方法
		 * @param buffer	要输出的字节
		 */
		public void write(byte[] buffer){
			try {
				monitorOutputStream.write(buffer);
				if(ISDEBUG) Log.d(TAG, "write: " + buffer);
				//将输出的信息也同时发送给UI
//				handler.obtainMessage(ActivityCht.MESSAGE_WRITE,-1,-1,buffer).sendToTarget();
			} catch (IOException e) {
				if(ISDEBUG) Log.e(TAG, "+++++write failed!+++++");
			}
		}
		
		/**
		 * 结束收发线程方法
		 */
		public void cancel(){
			try {
				monitorBluetoothSocket.close();
			} catch (IOException e) {
				if(ISDEBUG) Log.e(TAG, "+++++try to close monitor socket failed!+++++");
			}
		}
	}
	
}
